package com.foreveross.bsl.common.utils.ws.jaxrs;

import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.regex.Pattern;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.apache.cxf.jaxrs.ext.ParameterHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.collect.Maps;
import com.google.common.collect.Sets;

/**
 * JAX-RS的Map参数Handler，实现了从字符串到简单类型(包括Date)的转换,
 * 
 * @author Wangyi
 *
 */
public class MapParameterHandler implements ParameterHandler<Map<String, Object>> {
	
	private final static Logger log = LoggerFactory.getLogger(MapParameterHandler.class);

	private boolean convertToNearType = false;
	
	@Override
	public Map<String, Object> fromString(String s) {
		log.debug("map style string: {}", s);
		if(StringUtils.isEmpty(s)){
			return Maps.newHashMap();
		}
		//CXF对于Map将序列化成类似字符串：
		//“{birthday=Wed Aug 14 16:32:36 CST 2013, age=-29, name=wangyi, hobby=music, salary=1.111111000888E9}”
		StringBuilder sb=new StringBuilder(s);
		if(sb.charAt(0)=='{'){
			sb.deleteCharAt(0);
		}
		int lastIndex=sb.length()-1;
		if(sb.charAt(lastIndex)=='}'){
			sb.deleteCharAt(lastIndex);
		}
		
		//FIXED 解决了key或value中包含','或'='的问题
		StringTokenizer st=new StringTokenizer(sb.toString(), ",");
		StringBuilder keyValuePair=new StringBuilder();
		Map<String, String> origMap=Maps.newHashMap();
		int index;
		while(st.hasMoreTokens()){
			String t=st.nextToken().trim();
			index=t.indexOf("=");
			if(index!=-1){
				if(keyValuePair.length()>0){
					String[] kv=parseKeyValue(keyValuePair.toString());
					origMap.put(kv[0], kv[1]);
					keyValuePair.delete(0, keyValuePair.length());
				}
				keyValuePair.append(t);
			}
			else{
				keyValuePair.append(',').append(t);
			}
		}
		if(keyValuePair.length()>0){
			String[] kv=parseKeyValue(keyValuePair.toString());
			origMap.put(kv[0], kv[1]);
		}
		
		boolean convertToNearType=this.convertToNearType;
		String[] keyValues=StringUtils.split(sb.toString(), '=');
		Map<String, Object> map=Maps.newHashMapWithExpectedSize(keyValues.length);
		for(Map.Entry<String, String> entry : origMap.entrySet()){
			if(!convertToNearType){
				//如果WS接口的一个方法有个参数类型是Map<String,String>，eg: CheckinService.updateTags()方法
				//并且此类在反序列化时对value作了类型转换，即使方法的Map参数是Map<String,String>类型,但CXF在调用时还是会将
				//作过类型转换的Map隐性的设置过去。所以该类默认不转换value的类型
				map.put(entry.getKey(), entry.getValue());
			}
			else{
				map.put(entry.getKey(), parseToNearType(entry.getValue()));
			}
		}
		log.debug("parsed map: {}", map);
		return map;
	}
	
	private String[] parseKeyValue(String keyValuePair){
		int i=keyValuePair.indexOf("=");
		if(i==-1){
			throw new IllegalArgumentException("键值对数据有错，没有等号分隔:"+keyValuePair);
		}
		String key=keyValuePair.substring(0, i);
		String value=keyValuePair.substring(i+1);
		return new String[]{key, value};
	}
	
	public static void main(String[] args){
		//TODO need move to unit test
		String str="{地市=深圳=KKK, level=gold, 级别=特级'\"=, role=机长,管理员, city=gz}";
		MapParameterHandler mp=new MapParameterHandler();
		Map<String, Object> ret=mp.fromString(str);
		System.out.println(ret);
		str="{privileges=机长,管理员}";
		ret=mp.fromString(str);
		System.out.println(ret);
	}

	private final static Pattern PATTERN_INTEGER=Pattern.compile("^[\\+\\-]{0,1}[0-9]\\d*$");
	private final static Pattern PATTERN_FLOAT=Pattern.compile("^[\\+\\-]{0,1}[0-9]\\d*\\.\\d*$");
	private final static Pattern PATTERN_FLOAT_SN=Pattern.compile("^[\\+\\-]{0,1}[0-9]\\d*\\.\\d*[eE][\\+\\-]{0,1}\\d+$");
	private final static String[] DEFAULT_DATE_PATTERNS=new String[]{"yyyy-MM-dd HH:mm:ss", "EEE MMM dd HH:mm:ss zzz yyyy"};
	
	private Set<String> datePatterns=Sets.newHashSet(DEFAULT_DATE_PATTERNS);
	
	protected Object parseToNearType(String value){
		try {
			if(StringUtils.isAlphaSpace(value)){
				return value;
			}
			if(PATTERN_INTEGER.matcher(value).matches()){
				try {
					return Integer.parseInt(value);
				} catch (NumberFormatException e) {
					return Long.parseLong(value);
				}
			}
			if(PATTERN_FLOAT.matcher(value).matches()){
				return Float.parseFloat(value);
			}
			if(PATTERN_FLOAT_SN.matcher(value).matches()){
				return Double.parseDouble(value);
			}
			//FIXME 类似“Wed Aug 14 16:32:36 CST 2013”字符串的日期字符串已给定的pattern无法解析
			return DateUtils.parseDateStrictly(value, datePatterns.toArray(new String[0]));
		} catch (Exception e) {
			//ignore exception
			return value;
		}
	}
	
	public Set<String> getDatePatterns() {
		return datePatterns;
	}

	public void setDatePatterns(Set<String> datePatterns) {
		this.datePatterns.addAll(datePatterns);
	}

	/**
	 * @return the convertToNearType
	 */
	public boolean isConvertToNearType() {
		return convertToNearType;
	}

	/**
	 * @param convertToNearType the convertToNearType to set
	 */
	public void setConvertToNearType(boolean convertToNearType) {
		this.convertToNearType = convertToNearType;
	}

}
